#pragma once
#include "formatter.hpp"
#include "sink.hpp"
#include "message.hpp"
#include "asynclooper.hpp"
#include <mutex>
#include <atomic>
#include <stdarg.h>
#include <unordered_map>

namespace ns_log
{
    enum class SinkMode
    {
        LOCAL,
        ROMOTE,
        BOTH
    };

    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name, SinkMode sink_mode, LogLevel::value limit_level, const Formatter::ptr &formatter,
               const std::vector<FSLogSink::ptr> &fs_sinks, const std::vector<DBLogSink::ptr> &db_sinks, const std::vector<RemoteSink::ptr> &remote_sinks)
            : _logger_name(logger_name), _sink_mode(sink_mode), _limit_level(limit_level), _formatter(formatter), _fs_sinks(fs_sinks), _db_sinks(db_sinks), _remote_sinks(remote_sinks) {}

        virtual ~Logger() {}

        size_t fs_sink_size()
        {
            return _fs_sinks.size();
        }

        size_t db_sink_size()
        {
            return _db_sinks.size();
        }

        const std::string &name() { return _logger_name; }

        void debug(const std::string &file, size_t line, const std::string &body_format, ...)
        {
            // 1. 比较日志等级
            if (_limit_level > LogLevel::value::DEBUG)
                return;
            // 2. 解析参数
            va_list ap;
            va_start(ap, body_format);
            char *body = nullptr;
            if (-1 == vasprintf(&body, body_format.c_str(), ap))
            {
                std::cout << "debug: vasprintf失败\n";
                return;
            }
            va_end(ap);
            // 3. 复用
            construct(LogLevel::value::DEBUG, file, line, body);
        }

        void info(const std::string &file, size_t line, const std::string &body_format, ...)
        {
            // 1. 比较日志等级
            if (_limit_level > LogLevel::value::INFO)
                return;
            // 2. 解析参数
            va_list ap;
            va_start(ap, body_format);
            char *body = nullptr;
            if (-1 == vasprintf(&body, body_format.c_str(), ap))
            {
                std::cout << "info: vasprintf失败\n";
                return;
            }
            va_end(ap);
            // 3. 复用
            construct(LogLevel::value::INFO, file, line, body);
        }

        void warning(const std::string &file, size_t line, const std::string &body_format, ...)
        {
            // 1. 比较日志等级
            if (_limit_level > LogLevel::value::WARNING)
                return;
            // 2. 解析参数
            va_list ap;
            va_start(ap, body_format);
            char *body = nullptr;
            if (-1 == vasprintf(&body, body_format.c_str(), ap))
            {
                std::cout << "warning: vasprintf失败\n";
                return;
            }
            va_end(ap);
            // 3. 复用
            construct(LogLevel::value::WARNING, file, line, body);
        }

        void error(const std::string &file, size_t line, const std::string &body_format, ...)
        {
            // 1. 比较日志等级
            if (_limit_level > LogLevel::value::ERROR)
                return;
            // 2. 解析参数
            va_list ap;
            va_start(ap, body_format);
            char *body = nullptr;
            if (-1 == vasprintf(&body, body_format.c_str(), ap))
            {
                std::cout << "error: vasprintf失败\n";
                return;
            }
            va_end(ap);
            // 3. 复用
            construct(LogLevel::value::ERROR, file, line, body);
        }

        void fatal(const std::string &file, size_t line, const std::string &body_format, ...)
        {
            // 1. 比较日志等级
            if (_limit_level > LogLevel::value::FATAL)
                return;
            // 2. 解析参数
            va_list ap;
            va_start(ap, body_format);
            char *body = nullptr;
            if (-1 == vasprintf(&body, body_format.c_str(), ap))
            {
                std::cout << "fatal: vasprintf失败\n";
                return;
            }
            va_end(ap);
            // 3. 复用
            construct(LogLevel::value::FATAL, file, line, body);
        }

        void log_pure(const LogMessage::ptr &message)
        {
            if (_sink_mode == SinkMode::ROMOTE || _sink_mode == SinkMode::BOTH)
            {
                log_web(message);
            }
            if (_sink_mode == SinkMode::LOCAL || _sink_mode == SinkMode::BOTH)
            {
                log_db(message);
                if (!_fs_sinks.empty())
                {
                    // 2. 将LogMessage进行格式化
                    std::string real_message = _formatter->format(*message);
                    // 3. 复用log进行实际的日志落地
                    log_fs(real_message.c_str(), real_message.size());
                }
            }
        }

        // 因为下面的成员需要能够被子类所访问到, 因此我们不搞成private, 而是搞成protected
    protected:
        void construct(LogLevel::value level, const std::string &file, size_t line, char *body)
        {
            // 1. 构造LogMessage::ptr
            LogMessage::ptr message = std::make_shared<LogMessage>(level, file, line, _logger_name, body);
            // 2. 复用log_pure进行日志打印
            log_pure(message);
            // 3. free掉body,否则会内存泄漏
            free(body);
            body = nullptr;
        }

        virtual void log_fs(const char *data, size_t len) = 0;

        virtual void log_db(LogMessage::ptr message) = 0;

        virtual void log_web(LogMessage::ptr message) = 0;

        SinkMode _sink_mode;
        std::string _logger_name;
        std::mutex _mutex;
        std::atomic<LogLevel::value> _limit_level;
        std::vector<FSLogSink::ptr> _fs_sinks;
        std::vector<DBLogSink::ptr> _db_sinks;
        std::vector<RemoteSink::ptr> _remote_sinks;
        Formatter::ptr _formatter;
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name, SinkMode sink_mode, LogLevel::value limit_level, const Formatter::ptr &formatter,
                   const std::vector<FSLogSink::ptr> &fs_sinks, const std::vector<DBLogSink::ptr> &db_sinks, const std::vector<RemoteSink::ptr> &remote_sinks)
            : Logger(logger_name, sink_mode, limit_level, formatter, fs_sinks, db_sinks, remote_sinks)
        {
        }

        virtual void log_fs(const char *data, size_t len)
        {
            // 这里必须要加锁，因为存在多线程同时调用同一个日志器对象的log函数的情况
            std::unique_lock<std::mutex> ulock(_mutex);
            for (auto &sp : _fs_sinks)
            {
                sp->log_fs(data, len);
            }
        }

        virtual void log_db(LogMessage::ptr message)
        {
            // 这里必须要加锁，因为存在多线程同时调用同一个日志器对象的log函数的情况
            std::unique_lock<std::mutex> ulock(_mutex);
            for (auto &sp : _db_sinks)
            {
                sp->log_db(message);
            }
        }

        virtual void log_web(LogMessage::ptr message)
        {
            // 这里必须要加锁，因为存在多线程同时调用同一个日志器对象的log函数的情况
            std::unique_lock<std::mutex> ulock(_mutex);
            for (auto &sinks : _remote_sinks)
            {
                sinks->log_web(message);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name, SinkMode sink_mode, LogLevel::value limit_level, const Formatter::ptr &formatter,
                    const std::vector<FSLogSink::ptr> &fs_sinks, const std::vector<DBLogSink::ptr> &db_sinks, const std::vector<RemoteSink::ptr> &remote_sinks, AsyncType type = AsyncType::ASYNC_SAFE)
            : Logger(logger_name, sink_mode, limit_level, formatter, fs_sinks, db_sinks, remote_sinks),
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realFSLog, this, std::placeholders::_1),
                                                    std::bind(&AsyncLogger::realDBLog, this, std::placeholders::_1),
                                                    std::bind(&AsyncLogger::realWebLog, this, std::placeholders::_1), type))
        {
        }

        // 在业务线程看来，调用log函数之后，就意味着日志打印完毕
        // 对于异步日志器来说，业务线程只需要将日志写入日志缓冲区即可
        // 因此这里的log只需要将日志写入日志缓冲区即可
        virtual void log_fs(const char *data, size_t len)
        {
            _looper->push_fs(data, len);
        }

        virtual void log_db(LogMessage::ptr message)
        {
            _looper->push_db(message);
        }

        virtual void log_web(LogMessage::ptr message)
        {
            _looper->push_web(message);
        }

        void realFSLog(FSBuffer &buffer)
        {
            // 无需加锁，因为_looper内部本来就加锁了
            const char *begin = buffer.begin();
            size_t len = buffer.readableSize();
            for (auto &elem : _fs_sinks)
            {
                elem->log_fs(begin, len);
            }
            buffer.moveReader(len);
        }

        void realDBLog(DBBuffer &buffer)
        {
            // 无需加锁, 因为_looper内部本来就加锁了
            size_t readableSize = buffer.readableSize();
            for (auto &sink : _db_sinks)
            {
                sink->log_db(buffer.getBuffer(), readableSize);
            }
        }

        virtual void realWebLog(RemoteBuffer &buffer)
        {
            for (auto &elem : _remote_sinks)
            {
                elem->log_web(buffer.getBuffer());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    class LoggerBuilder
    {
    public:
        using ptr = std::shared_ptr<LoggerBuilder>;

        LoggerBuilder() : _logger_type(LoggerType::SYNC), _limit_level(LogLevel::value::DEBUG) {}
        virtual ~LoggerBuilder() {}

        void buildSinkMode(SinkMode sink_mode)
        {
            _sink_mode = sink_mode;
        }

        void buildLoggerName(const std::string &logger_name)
        {
            _logger_name = logger_name;
        }

        void buildLoggerType(LoggerType logger_type)
        {
            _logger_type = logger_type;
        }

        void buildLimitLevel(LogLevel::value limit_level)
        {
            _limit_level = limit_level;
        }

        void buildFormatter(const std::string &pattern = "")
        {
            if (pattern.empty())
                _formatter = std::make_shared<Formatter>();
            else
                _formatter = std::make_shared<Formatter>(pattern);
        }

        template <class SinkType, class... Args>
        void buildFSLogSink(Args &&...args)
        {
            _fs_sinks.push_back(LogSinkFactory::create_fs<SinkType>(std::forward<Args>(args)...));
        }

        template <class SinkType, class... Args>
        void buildDBLogSink(Args &&...args)
        {
            _db_sinks.push_back(LogSinkFactory::create_db<SinkType>(std::forward<Args>(args)...));
        }

        void buildAsyncType(AsyncType async_type)
        {
            _async_type = async_type;
        }

        template <class SinkType>
        void buildSinkStr(std::vector<std::string> sink_str)
        {
            std::string type_str;
            if constexpr (std::is_same_v<SinkType, ns_log::StdoutSink>)
                type_str = "StdoutSink";
            else if constexpr (std::is_same_v<SinkType, ns_log::FileSink>)
                type_str = "FileSink";
            else if constexpr (std::is_same_v<SinkType, ns_log::RollSinkBySize>)
                type_str = "RollSinkBySize";
            else if constexpr (std::is_same_v<SinkType, ns_log::SqliteSink>)
                type_str = "SqliteSink";
            else if constexpr (std::is_same_v<SinkType, ns_log::MySQLSink>)
                type_str = "MySQLSink";
            else
            {
                std::cout << "buildSinkStr失败,因为遇到了未知的SinkType类型\n";
                return;
            }
            sink_str.insert(sink_str.begin(), type_str);
            _sinks_vec.push_back(std::move(sink_str));
        }

        virtual Logger::ptr build() = 0;

        std::string get_pattern()
        {
            return _formatter.get() == nullptr ? "" : _formatter->getPattern();
        }

    protected:
        // 要求子类在重写build时，检查_sink_mode, 如果需要进行网络落地，则调用该函数
        virtual void buildWebLogSink()
        {
            _remote_sinks.push_back(std::make_shared<RemoteSink>(_logger_name, _logger_type, _limit_level, get_pattern(), _async_type, _sinks_vec));
        }

        // 允许一个建造者在调用build接口构造完对象之后将建造者当中保存的原数据进行重置
        void reset()
        {
            _sink_mode = SinkMode::LOCAL;
            _logger_name.clear();
            _logger_type = LoggerType::SYNC;
            _limit_level = LogLevel::value::DEBUG;
            _async_type = AsyncType::ASYNC_SAFE;
            _fs_sinks.clear();
            _db_sinks.clear();
            _remote_sinks.clear();
            _sinks_vec.clear();
            _formatter.reset();
        }

        SinkMode _sink_mode = SinkMode::LOCAL;
        std::string _logger_name;
        LoggerType _logger_type = LoggerType::SYNC;
        AsyncType _async_type = AsyncType::ASYNC_SAFE;
        LogLevel::value _limit_level = LogLevel::value::DEBUG;
        std::vector<FSLogSink::ptr> _fs_sinks;
        std::vector<DBLogSink::ptr> _db_sinks;
        std::vector<RemoteSink::ptr> _remote_sinks;
        Formatter::ptr _formatter;
        std::vector<std::vector<std::string>> _sinks_vec;
    };

    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        virtual Logger::ptr build()
        {
            if (_logger_name.empty())
            {
                std::cout << "日志器名称为空！！\n";
                abort();
            }
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_fs_sinks.empty() && _db_sinks.empty())
            {
                _fs_sinks.push_back(LogSinkFactory::create_fs<StdoutSink>());
            }
            if (_sink_mode == SinkMode::ROMOTE || _sink_mode == SinkMode::BOTH)
            {
                this->buildWebLogSink();
            }
            Logger::ptr ret;
            if (_logger_type == LoggerType::SYNC)
            {
                ret = std::make_shared<SyncLogger>(_logger_name, _sink_mode, _limit_level, _formatter, _fs_sinks, _db_sinks, _remote_sinks);
            }
            else
            {
                // 后面实现完异步日志器之后完善
                ret = std::make_shared<AsyncLogger>(_logger_name, _sink_mode, _limit_level, _formatter, _fs_sinks, _db_sinks, _remote_sinks, _async_type);
            }
            // 重置
            this->reset();
            return ret;
        }
    };

    const std::string default_logger_name = "_default_logger";

    // 线程安全的全局单例对象
    class LoggerManager
    {
    public:
        static LoggerManager &getInstance()
        {
            static LoggerManager logger_manager;
            return logger_manager;
        }

        // 增删查
        void addLogger(const Logger::ptr &logger)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            _logger_map.insert(std::make_pair(logger->name(), logger));
        }

        void delLogger(const std::string &logger_name)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            _logger_map.erase(logger_name);
        }

        Logger::ptr getLogger(const std::string &logger_name)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            auto iter = _logger_map.find(logger_name);
            if (iter == _logger_map.end())
                return Logger::ptr();
            else
                return iter->second;
        }

        bool existLogger(const std::string &logger_name)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            return _logger_map.count(logger_name) > 0;
        }

        Logger::ptr default_logger()
        {
            // 无需加锁,因为没人修改它
            return _default_logger;
        }

    private:
        // 构造和析构私有
        LoggerManager()
        {
            // 创建默认日志器
            // 这里必须是局部日志器, 如果是全局日志器的话, 调用其build创建对象之后, 其会将该日志器添加到单例对象当中
            // 而此时单例对象还没有创建好, 此时就发生相互依赖问题导致的死循环
            LoggerBuilder::ptr builder = std::make_shared<LocalLoggerBuilder>();
            builder->buildLoggerName(default_logger_name);
            _default_logger = builder->build();
            _logger_map.insert(std::make_pair(default_logger_name, _default_logger));
        }
        ~LoggerManager() {}
        // 禁掉拷贝和赋值
        LoggerManager(const LoggerManager &) = delete;
        const LoggerManager &operator=(const LoggerManager &) = delete;

        std::mutex _mutex;
        Logger::ptr _default_logger;
        std::unordered_map<std::string, Logger::ptr> _logger_map;
    };

    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        virtual Logger::ptr build()
        {
            if (_logger_name.empty())
            {
                std::cout << "日志器名称为空！！\n";
                abort();
            }
            // 查找全局当中是否有该日志器
            Logger::ptr ret = LoggerManager::getInstance().getLogger(_logger_name);
            if (ret.get() != nullptr)
                return ret;
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_fs_sinks.empty() && _db_sinks.empty())
            {
                _fs_sinks.push_back(LogSinkFactory::create_fs<StdoutSink>());
            }
            if (_sink_mode == SinkMode::ROMOTE || _sink_mode == SinkMode::BOTH)
            {
                this->buildWebLogSink();
            }
            if (_logger_type == LoggerType::SYNC)
            {
                ret = std::make_shared<SyncLogger>(_logger_name, _sink_mode, _limit_level, _formatter, _fs_sinks, _db_sinks, _remote_sinks);
            }
            else
            {
                // 后面实现完异步日志器之后完善
                ret = std::make_shared<AsyncLogger>(_logger_name, _sink_mode, _limit_level, _formatter, _fs_sinks, _db_sinks, _remote_sinks, _async_type);
            }
            // 重置
            this->reset();
            LoggerManager::getInstance().addLogger(ret);
            return ret;
        }
    };

}
